#pragma once

#include <iostream>
#include <string>
#include <sys/select.h>

#include "Log.hpp"
#include "Socket.hpp"

using namespace Net_Work;

const static int gdefaultport = 8888;
const static int gbacklog = 8;
const static int num = sizeof(fd_set) * 8;

class SelectServer
{
private:
    void HandlerEvent(fd_set &rfds)
    {
        for (int i = 0; i < num; i++)
        {
            if (_rfds_array[i] == nullptr)
                continue;

            // 合法的fd
            // 读事件分两种，一类是新链接的到来，一类是新数据的到来
            int fd = _rfds_array[i]->GetSocket();
            if (FD_ISSET(fd, &rfds))
            {
                // 读事件就绪 -> 新链接的到来
                if (fd == _listensock->GetSocket())
                {
                    lg.LogMessage(Info, "get a new link\n");
                    std::string clientip;
                    uint16_t clientport;
                    // 这里不会阻塞，因为select已经检测到listensock就绪了
                    Socket *sock = _listensock->AcceptConnection(&clientip, &clientport);
                    if (!sock)
                    {
                        lg.LogMessage(Error, "accept error\n");
                        continue;
                    }
                    lg.LogMessage(Info, "get a client, client info is# %s:%d, fd:%d\n", clientip.c_str(), clientport, sock->GetSocket());
                    // 获取成功了，但是我们不能直接读写，底层的数据不确定是否就绪
                    // 新链接fd到来时，要把新链接fd交给select托管 --- 只需要添加到数组_rfds_array中即可
                    int pos = 0;
                    for (; pos < num; pos++)
                    {
                        if (_rfds_array[pos] == nullptr)
                        {
                            _rfds_array[pos] = sock;
                            break;
                        }
                    }
                    if (pos == num)
                    {
                        sock->CloseSocket();
                        delete sock;
                        lg.LogMessage(Warning, "server is full ... !\n");
                    }
                }
                // 新数据的到来
                else
                {
                    std::string buffer;
                    bool res = _rfds_array[i]->Recv(&buffer, 1024);
                    if(res)
                    {
                        lg.LogMessage(Info, "client say# %s\n", buffer.c_str());
                        buffer.clear();
                    }
                    else
                    {
                        lg.LogMessage(Warning, "client quit, maybe close or error, close fd: %d\n", _rfds_array[i]->GetSocket());
                        _rfds_array[i]->CloseSocket();
                        delete _rfds_array[i];
                        _rfds_array[i] = nullptr;
                    }
                }
            }
        }
    }

public:
    SelectServer(int port = gdefaultport)
        : _port(port), _listensock(new TcpSocket())
    {
    }

    void InitServer()
    {
        _listensock->BuildListenSocketMethod(_port, gbacklog);
        for (int i = 0; i < num; i++)
        {
            _rfds_array[i] = nullptr;
        }
        _rfds_array[0] = _listensock.get();
    }

    void Loop()
    {
        _isrunning = true;

        while (_isrunning)
        {
            // 不能直接accept新连接，而是要将selete交给selete, 只有selete有资格知道IO事件有没有就绪
            fd_set rfds;
            FD_ZERO(&rfds);
            int max_fd = _listensock->GetSocket();
            for (int i = 0; i < num; i++)
            {
                if (_rfds_array[i] == nullptr)
                {
                    continue;
                }
                else
                {
                    int fd = _listensock->GetSocket();
                    FD_SET(fd, &rfds); // 添加所有合法的fd到rfds集合中
                    if (max_fd < fd)   // 更新最大fd
                    {
                        max_fd = fd;
                    }
                }
            }

            // 遍历数组，1.找最大值 2.合法的fd添加到rfds集合中

            // 定义时间
            struct timeval timeout = {0, 0};
            // rfds本质是一个输入输出型参数，rfds是在select调用返回的时候，不断被修改，所以每次都要重置
            PrintDebug();
            int n = select(max_fd + 1, &rfds, nullptr, nullptr, /*&timeout*/ nullptr);
            switch (n)
            {
            case 0:
                lg.LogMessage(Info, "select timeout ... last time: %u.%u\n", timeout.tv_sec, timeout.tv_usec);
                break;
            case -1:
                lg.LogMessage(Error, "select error !!! \n");
            default:
                lg.LogMessage(Info, "select success, begin event handler, last time: %u.%u\n", timeout.tv_sec, timeout.tv_usec);
                HandlerEvent(rfds);
                break;
            }
        }

        _isrunning = false;
    }

    void stop()
    {
        _isrunning = false;
    }

    void PrintDebug()
    {
        std::cout << "current select rfds list is: ";
        for (int i = 0; i < num; i++)
        {
            if (_rfds_array[i] == nullptr)
                continue;
            else
                std::cout << _rfds_array[i]->GetSocket() << " ";
        }
        std::cout << std::endl;
    }

    ~SelectServer()
    {
    }

private:
    std::unique_ptr<Socket> _listensock;
    int _port;
    bool _isrunning;
    Socket *_rfds_array[num];
};
